// Copyright 2013 Daniel de Kok
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.gitee.wsl.text.dictomaton.collections

import com.gitee.wsl.collections.list.toArray
import com.gitee.wsl.collections.bean.SampleMapEntry
import com.gitee.wsl.collections.map.TreeMap
import com.gitee.wsl.text.dictomaton.DictionaryBuilder
import com.gitee.wsl.text.dictomaton.DictionaryBuilderException
import com.gitee.wsl.text.dictomaton.PerfectHashDictionary

/**
 * An immutable mapping from [String] to an object with the type `V`, where keys are compactly stored
 * using a finite state automaton.
 */
class ImmutableStringObjectDictionaryMap<V>(
    keys: PerfectHashDictionary,
    values: Array<V>
) : AbstractMap<String, V>() {
    private val d_keys: PerfectHashDictionary = keys
    private val d_values: Array<V> = values

    /**
     * A builder for [ImmutableStringIntMap]. Mappings
     * can be added to the builder using the [.put] and
     * [.putAll] methods. The [ImmutableStringIntMap]
     * can then be constructed using the [.build] method.
     */
    class Builder<V:Comparable<V>> {
        private val d_map: TreeMap<String, V> = TreeMap()

        /**
         * Put a key/value pair.
         */
        //@Synchronized
        fun put(key: String, value: V): Builder<V> {
            d_map.put(key, value)
            return this
        }

        /**
         * Put all key/value pairs from a [java.util.Map].
         */
        //@Synchronized
        fun putAll(map: Map<String, V>): Builder<V> {
            d_map.putAll(map)
            return this
        }

        /**
         * Construct a [ImmutableStringIntMap].
         */
        //@Synchronized
        @Throws(DictionaryBuilderException::class)
         fun build(): ImmutableStringObjectDictionaryMap<V> {
            val dict: PerfectHashDictionary = DictionaryBuilder().addAll(d_map.keys).buildPerfectHash(false)

//            val values = d_map.values
//            val array = LazyArrayList<V>(values.size)
//            values.forEachIndexed { index, v ->
//                array[index] = v
//            }

            return ImmutableStringObjectDictionaryMap(dict, d_map.values.toArray())
        }
    }

    /**
     * A builder for [ImmutableStringObjectDictionaryMap]. Mappings can be added to the builder using the [.put] and
     * [.putAll] methods. The [ImmutableStringObjectDictionaryMap] can then be constructed using the [.build]
     * method. **Note:** This builder assumes that entries are put in key order. This additional assumption makes
     * the builder more efficient than [Builder].
     */
//    class OrderedBuilder<V> {
//        private val dictionaryBuilder: DictionaryBuilder = DictionaryBuilder()
//
//        private val values: ArrayList<V> = ArrayList()
//
//        /**
//         * Put a key/value pair.
//         */
//        //@Synchronized
//        @Throws(DictionaryBuilderException::class)
//        fun put(key: String, value: V): OrderedBuilder<*> {
//            dictionaryBuilder.add(key)
//            values.add(value)
//            return this
//        }
//
//        /**
//         * Put all key/value pairs from a [Map]. The map should be an ordered map (by key). If
//         * not, a [IllegalArgumentException] is thrown.
//         */
//        //@Synchronized
//        @Throws(DictionaryBuilderException::class)
//        fun putAll(map: SortedMap<String, V>): OrderedBuilder<*> {
//            require(map.comparator() == null) { "SortedMap does not use the natural ordering of its keys" }
//
//            values.ensureCapacity(values.size+ map.size)
//
//            for (entry in map.entrySet()) {
//                dictionaryBuilder.add(entry.getKey())
//                values.add(entry.getValue())
//            }
//
//            return this
//        }
//
//        /**
//         * Construct a [ImmutableStringIntMap].
//         */
//        //@Synchronized
//        @Throws(DictionaryBuilderException::class)
//        fun build(): ImmutableStringObjectMap<V> {
//            val dict: PerfectHashDictionary = dictionaryBuilder.buildPerfectHash(false)
//
////            val arr = kotlin.arrayOfNulls<Any>(values.size) as Array<V?>
////
////            for (i in 0..< values.size ) arr[i] = values[i]
//
//            val array = LazyArrayList<V>(values)
//
//            return ImmutableStringObjectMap(dict, array.toArray())
//        }
//    }

    private inner class EntrySet : AbstractSet<Map.Entry<String, V>>() {
        private inner class EntrySetIterator : Iterator<Map.Entry<String, V>> {
            private val d_keyIter: Iterator<String> = d_keys.iterator()


            override fun hasNext(): Boolean {
                return d_keyIter.hasNext()
            }

            override fun next(): Map.Entry<String, V> {
                val key = d_keyIter.next()
                val idx: Int = d_keys.number(key) - 1
                return SampleMapEntry(key, d_values[idx])
            }

        }

        override val size: Int
            get() = d_keys.size


        override fun contains(element: Map.Entry<String, V>): Boolean {

//            if (o == null) return false
//
//            if (o !is Map.Entry<*,*>) return false
//
//            val e = o as Map.Entry
//
//            // Key cannot be null.
//            if (e.key == null) return false
//
//            if (e.key !is String) return false
//
//            val key = e.getKey() as String?
//            val value: Object? = e.getValue()

            val hash: Int = d_keys.number(element.key)

            // Does not contain the key.
            if (hash == -1) return false

            return d_values[hash - 1]!! == element.value
        }

        override fun isEmpty(): Boolean {
            return d_keys.isEmpty()
        }

        override fun iterator(): Iterator<Map.Entry<String, V>> {
            return EntrySetIterator()
        }

    }


    private inner class ObjectArrayList : AbstractList<V>() {

        override fun get(index: Int): V {
            return d_values[index]
        }

        override val size: Int
            get() = d_values.size

    }


    fun clear() {
        throw UnsupportedOperationException()
    }


    override fun containsKey(key: String): Boolean {
        return d_keys.contains(key)
    }

    override val entries: Set<Map.Entry<String, V>>
        get() = EntrySet()

    override val values: Collection<V>
        get() = ObjectArrayList()

    override val keys: Set<String>
        get() = d_keys

    override fun get(key: String): V? {
//        if (o !is String) return null
//
//        val key = o as String

        val hashcode: Int = d_keys.number(key)
        if (hashcode == -1) return null

        return d_values[hashcode - 1]
    }

    override fun isEmpty(): Boolean {
        return d_keys.isEmpty()
    }


    fun put(k: String, v: V): V {
        throw UnsupportedOperationException()
    }


    fun putAll(m: Map<String,  V>) {
        throw UnsupportedOperationException()
    }


    fun remove(key: String): V {
        throw UnsupportedOperationException()
    }

    /**
     * Get an iterator over the keys in the mapping.
     */
    fun keyIterator(): Iterator<String> {
        return d_keys.iterator()
    }



}
